function UndoManager(textModel){
    this.init$Eventable();
    this.reset();
    this.$textModel = textModel;
    this.$deltas = [];
    this.on(textModel, 'Changed', this._onTextChanged);
}

UndoManager.prototype = {
    hasUndo: function (){
        return this.$undoStack.length > 0;
    },
    hasRedo: function (){
        return this.$redoStack.length > 0;
    },
    save: function (){
        if (this.$deltas.length) {
            this.$undoStack.push(this.$deltas);
            this.$deltas = [];
            this.$redoStack = [];
        }
    },
    reset: function (){
        this.$undoStack = [];
        this.$redoStack = [];
    },
    undo: function (){
        this.save();
        var deltas = this.$undoStack.pop();
        if (deltas) {
            this.undoDeltas(deltas);
            this.$redoStack.push(deltas);
        }
    },
    redo: function (){
        var deltas = this.$redoStack.pop();
        if (deltas) {
            this.redoDeltas(deltas);
            this.$undoStack.push(deltas);
        }
    },
    undoDeltas: function (deltas){
        this._prevent = true;
        var k = deltas.length;
        while (k --) {
            var delta = deltas[k];
            this.$textModel.setText(delta.deletedText,
                delta.offset, delta.offset + delta.insertedText.length);
        }
        this._prevent = false;
    },
    redoDeltas: function (deltas){
        this._prevent = true;
        for (var i=0; i<deltas.length; i++) {
            var delta = deltas[i];
            this.$textModel.setText(delta.insertedText,
                delta.offset, delta.offset + delta.deletedText.length);
        }
        this._prevent = false;
    },
    _onTextChanged: function (evt){
        if (!this._prevent) {
            this.$deltas.push({
                offset: evt.offset,
                deletedText: evt.deletedText,
                insertedText: evt.insertedText
            });
        }
    }
};

Utils.extend(UndoManager, Eventable);
